home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-05-23 | 25.1 KB | 804 lines | [TEXT/PJMM] |
- {--------- OFFSCREEN TOYS with SAT ---------}
- {by Ingemar Ragnemalm 1994}
-
- {Offscreen Toys is a nice little demo I made to make a SAT-like demo with complete source}
- {code, independent of any libraries (except Apple's). Now, this should be easier to do with}
- {SAT, right? Well, partially so, but while adapting it to SAT, I ran into a minor flaw that}
- {SAT had (and doesn't have from version 2.1 and up), namely that drawing took place after}
- {moving sprites, but before checking for collisions, which didn't look too good in programs}
- {where sprites bounce off each other. After fixing this flaw, I would say that the SAT}
- {version is indeed a bit better than the independent version. The result is quite a bit faster}
- {and with asynch sound.}
- {}
- {As yet another SAT demo, what does it give us?}
- {• If you wonder what it costs to have a real event loop, or good collision handling, this demo}
- {shows that pretty well.}
- {• Demonstrates a moveable window AND fast mode at the same time, with the precautions}
- {that demands when moving the window. (The window mustn't be moved outside the screen,}
- {some internal SAT variables – that you otherwise should never care about – must be adjusted,}
- {and we must stay word-aligned to make it work in b/w).}
- {}
- {I don't consider this demo final in any way. Known flaws:}
- {• I have made some mistakes in the port-setting. Nothing fatal, I think. FIXED.}
- {• I don't protect the user from moving the window outside the screen, which might be fatal}
- {when the SAT blitters are turned on. FIXED.}
- {• Like in the "real" Offscreen Toys, there is a bug that causes sprites to disappear for a short}
- {while, since the position gets negative. No big deal.}
- {• The code for the marble should be separated from the main program, to make the code easier}
- {to follow.}
- {I'll fix those things when I find time and inspiration for it - but you are welcome to do it if}
- {you want!}
-
- program OffscreenToysSAT;
- uses
- {$ifc UNDEFINED THINK_PASCAL}
- Types, QuickDraw, Events, Windows, Dialogs, Fonts, DiskInit, {PackIntf,}
- TextEdit, Traps, {Desk,}
- Devices, Memory, SegLoad, Scrap, ToolUtils, {OSEvents,}
- OSUtils, Menus, Resources, StandardFile, GestaltEqu, Files, Errors,
- {$elsec}
- InterfacesUI,
- {$endc}
- SAT;
-
- { --- PART 1: Variables and constants: -----------------------------------------}
-
- {A redefined sprite record:}
- type
- OTSpritePtr = ^OTSprite;
- OTSprite = record
- { Variables that you should change as appropriate }
- kind: Integer; { Used for identification. >0: friend. <0 foe }
- position: Point;
- hotRect, hotRect2: Rect; { Tells how large the sprite is; hotRect is centered around origo }
- {hotRect is set by you. hotRect2 is offset to the current position.}
- face: FacePtr; { Pointer to the Face (appearance) to be used. }
- task: ProcPtr; { Callback-routine, called once per frame. If task=nil, the sprite is removed. }
- hitTask: ProcPtr; { Callback in collisions. }
- destructTask: ProcPtr; { Called when a sprite is disposed. (Usually nil.) }
- clip: RgnHandle; {Clip region to be used when this sprite is drawn.}
- { SAT variables that you shouldn't change: }
- oldpos: Point; {The 'task' routine is not allowed to change this! }
- next, prev: SpritePtr; {You may change them in your own sorting routine, but be careful if you do.}
- r, oldr: Rect; {Rectangle telling where to draw. Avoid messing with it.}
- oldFace: FacePtr; {Used by RunSAT2}
- dirty: Boolean; {Used by RunSAT2}
- {Variables for internal use by the sprites. Use as you please. Edit as necessary - this is merely a default}
- {set, enough space for most cases - but if you change the size of the record, call SetSpriteSize immediately}
- {after initializing (before any sprites are created)!}
- layer: integer; {For layer-sorting. When not used for that, use freely.}
- speed: Point; { Can be used for speed, but not necessarily. }
- mode: integer; { Usually used for different modes and/or to determine what image to show next. }
- fixedPos: Point; {Fixed-point position}
- appLong: Longint; {Longint for free use by the application.}
- end;
-
- const
- kAppleID = 128;
- kFileID = 129;
- kMBarHeight = 20; { We assume 20 pixels menu bar for window sizing and dragging.}
-
- kWindID = 128; { Window resource ID }
- kAboutAlertID = 128; { Alert resource ID }
-
- kSpriteNumber = 5; { Number of moving objects }
- var
- gColorQDFlag: Boolean; {True if 32-bit QD exists. If not, we run everything in b/w.}
- gHasWNE: Boolean; {True if we can use WaitNextEvent}
- gSoundFlag: Boolean; {True if we want sound.}
- gFast: Boolean;
-
- gWhoa: Boolean; {True when we want to quit}
- gCollisionFlag: Boolean; {Collisions or not?}
-
- {Menu handles}
- appleMenu, fileMenu: MenuHandle;
-
- {The window we'll be using}
- gWind: WindowPtr;
-
- {Our two offscreens:}
- {offScreen, backScreen: GrafPtr;}
-
- {A cicn loded as a face}
- gCicn: FacePtr;
- kgck: Handle;
-
- {Sprite information. In real games, I prefer making a linked list of records, like I do in}
- {SAT, and a lot more information for each, but here we want it *simple*.}
- {- position: The positions in local coordinates for the window}
- {- fixedPos: 16 times position, which gives us fixed-point numbers}
- {- speed: Speed vectors that is added to fixedPos for every frame}
- {- r: Rectangles used in drawing, for remembering what part of the screen to update}
- {position, fixedPos: array[1..kSpriteNumber] of Point;}
- {speed: array[1..kSpriteNumber] of Point;}
- {r: array[1..kSpriteNumber] of Rect;}
- {}
- {…but, since this now uses SAT, the linked list is built-in, so all the variables above are in}
- {the sprite record!}
-
- {A longint that shows how big the "bowl" is (assumes circular, not oval, bowl!):}
-
- gBowlSize: Longint;
-
- { --- PART 2: Various general, reuseable routines, mostly glue: ---------------------}
-
- {BailOut: Emergency exit. We go here on most errors. Real programs report what the}
- {problem is. You may wish to put a breakpoint in BailOut when debugging.}
-
- procedure BailOut;
- begin
- SysBeep(1); {Minimal error message. Use alert in real programs.}
- halt;
- end;
-
- {Several functions deleted since SAT handles what they do.}
-
- { --- PART 3: Application specific routines: ---------------------------------}
-
- {mouse clicks, keydowns, background tasks and update events: This is where all}
- {the action is. :-) I include some empty procedures for you to fill in if you want to}
- {use this demo as application shell.}
-
- {Mouse click in window content}
-
- procedure DoMouse (where: Point; modifiers: Longint);
- begin
- end;
-
- {Keydown.}
-
- procedure DoKey (theKey: Char; modifiers: Longint);
- begin
- end;
-
- const
- kWallBounce = 7; {1/10-ths of speed kept after wallbounce}
- kBallDiameterSquared = 32 * 32; {Diameter 32, squared}
-
-
- {A rather boring subroutine that moves the sprites i and j away from each other.}
- {I almost didn't want to put this in, making the demo unnecessarily big, but I wanted}
- {decent collisions. If anyone can suggest a better (simpler) collision handling for circular}
- {objects, I'd be happy to put it in.}
- {I use a line drawing algorithm for it. I'm sure there are better ways. This is of questionable}
- {value as reuseable code - depends on your application.}
- procedure Separate (i, j: OTSpritePtr);
- var
- initVector, nowVector: Point;
- absH, absV: integer;
- moveH, moveV: integer;
- frac: integer;
- {Normal signum function (which I don't think is in the libs)}
- function Sgn (x: integer): integer;
- begin
- if x > 0 then
- Sgn := 1
- else if x < 0 then
- Sgn := -1
- else
- Sgn := 0;
- end;
-
- begin {Separate}
- frac := 0;
- initVector.h := i^.position.h - j^.position.h;
- initVector.v := i^.position.v - j^.position.v;
- absH := abs(initVector.h);
- absV := abs(initVector.v);
- moveH := Sgn(initVector.h);
- moveV := Sgn(initVector.v);
- if moveH = 0 then
- if moveV = 0 then
- moveV := 1;
- repeat
- if absH > absV then
- begin
- i^.position.h := i^.position.h + moveH;
- j^.position.h := j^.position.h - moveH;
- frac := frac + absV;
- if frac > absH then
- begin
- i^.position.v := i^.position.v + moveV;
- j^.position.v := j^.position.v - moveV;
- frac := frac - absH;
- end
- end
- else
- begin
- i^.position.v := i^.position.v + moveV;
- j^.position.v := j^.position.v - moveV;
- frac := frac + absH;
- if frac > absV then
- begin
- i^.position.h := i^.position.h + moveH;
- j^.position.h := j^.position.h - moveH;
- frac := frac - absV;
- end
- end;
- nowVector.h := i^.position.h - j^.position.h;
- nowVector.v := i^.position.v - j^.position.v;
- until longint(nowVector.h) * nowVector.h + longint(nowVector.v) * nowVector.v > kBallDiameterSquared;
- i^.fixedPos.h := BSL(i^.position.h, 4); {Make fixedPos by shifting in the 4 binary "decimals"}
- i^.fixedPos.v := BSL(i^.position.v, 4);
-
- j^.fixedPos.h := BSL(j^.position.h, 4); {Make fixedPos by shifting in the 4 binary "decimals"}
- j^.fixedPos.v := BSL(j^.position.v, 4);
-
- end;{Separate}
-
- {Split a vector (v1) into one component parallell to another vector (direction) and one}
- {orthogonal to it.}
- procedure SplitVector (v1, direction: Point; var parallell, normal: Point);
- var
- l2, v1pr: Longint;
- begin
- {parallell := direction * (v1 DOT direction) /|direction|**2}
- {normal := v1 - parallell}
-
- l2 := direction.h * direction.h + direction.v * direction.v; {Squared length of "direction"}
- v1pr := v1.h * direction.h + v1.v * direction.v; {Scalar product}
-
- parallell.h := direction.h * v1pr div l2;
- parallell.v := direction.v * v1pr div l2;
- normal.h := v1.h - parallell.h;
- normal.v := v1.v - parallell.v;
- end; {SplitVector}
-
- procedure HandleMarble (i: OTSpritePtr);
- var
- vector: Point;
- begin
- {Modify fixed-point position by speed}
- i^.fixedPos.h := i^.fixedPos.h + i^.speed.h;
- i^.fixedPos.v := i^.fixedPos.v + i^.speed.v;
-
- {Make position by shifting away the 4 binary "decimals"}
- if i^.fixedPos.h >= 0 then
- i^.position.h := BSR(i^.fixedPos.h, 4)
- else
- i^.position.h := BitOr(BSR(i^.fixedPos.h, 4), $f000);
- if i^.fixedPos.v >= 0 then
- i^.position.v := BSR(i^.fixedPos.v, 4)
- else
- i^.position.v := BitOr(BSR(i^.fixedPos.v, 4), $f000);
-
- {Outside the allowed area?}
- if i^.position.h < 0 then
- begin
- i^.speed.h := abs(i^.speed.h) * kWallBounce div 10 + 1;
- { i^.position.h := 0;}
- end;
- if i^.position.v < 0 then
- begin
- i^.speed.v := abs(i^.speed.v) * kWallBounce div 10 + 1;
- { i^.position.v := 0; }
- end;
- if i^.position.h + gCicn^.iconMask.bounds.right > gSAT.offScreen.port^.portRect.right then
- begin
- i^.speed.h := -abs(i^.speed.h) * kWallBounce div 10 - 1;
- { i^.position.h := gSAT.offScreen^.portRect.right - gCicn^.iconMask.bounds.right; }
- end;
- if i^.position.v + gCicn^.iconMask.bounds.bottom > gSAT.offScreen.port^.portRect.bottom then
- begin
- i^.speed.v := -abs(i^.speed.v) * kWallBounce div 10 - 1;
- { i^.position.v := gSAT.offScreen^.portRect.bottom - gCicn^.iconMask.bounds.bottom; }
- end;
-
- {Are we in the bowl? If we are, accelerate towards the center.}
- vector.h := i^.position.h + 16 - BSR(gSAT.offScreen.port^.portRect.right, 1);
- vector.v := i^.position.v + 16 - BSR(gSAT.offScreen.port^.portRect.bottom, 1);
- if (vector.h * vector.h + vector.v * vector.v) < gBowlSize then
- begin
- i^.speed.h := i^.speed.h - vector.h div 2;
- i^.speed.v := i^.speed.v - vector.v div 2;
- end;
- end; {position/speed loop}
-
- procedure HitMarble (i, j: OTSpritePtr);
- var
- vector: Point;
- squaredLength: Longint;
- p1, p2, n1, n2, tmpSpeed: Point;
- begin
- if not gCollisionFlag then
- exit(HitMarble);
-
- {Find the vector between them}
- vector.h := i^.position.h - j^.position.h;
- vector.v := i^.position.v - j^.position.v;
- squaredLength := longint(vector.h) * vector.h + longint(vector.v) * vector.v;
- {If it is shorter than the diameter of a ball…}
- if squaredLength < kBallDiameterSquared then
- begin
- {Move them away from each other}
- Separate(i, j);
-
- {if false then}
- begin
- {Swap the speed components that are parallell to "vector" (this allows for "touches", very}
- {nice and realistic bounces)}
- SplitVector(i^.speed, vector, p1, n1);
- SplitVector(j^.speed, vector, p2, n2);
-
- j^.speed.h := p1.h + n2.h;
- j^.speed.v := p1.v + n2.v;
-
- i^.speed.h := p2.h + n1.h;
- i^.speed.v := p2.v + n1.v;
- end;
-
- {Old Offscreen Toys just swapped the speed, as commented out below. This is not as realistic.}
- {tmpSpeed := i^.speed;}
- {i^.speed := j^.speed;}
- {j^.speed := tmpSpeed;}
-
- {Play a sound. SAT is good at playing sounds!}
- if gSoundFlag then
- SATSoundPlay(kgck, 1, false);
-
- end;
- end; {collision loop}
-
- procedure SetupMarble (i: OTSpritePtr);
- begin
- i^.fixedPos.h := BSL(i^.position.h, 4);
- i^.fixedPos.v := BSL(i^.position.v, 4);
- i^.speed.h := Random mod 32;
- i^.speed.v := Random mod 32;
- i^.task := @HandleMarble;
- i^.hitTask := @HitMarble;
- i^.face := gCicn;
- SetRect(i^.hotRect, 0, 0, 32, 32);
- end;
-
- {A static variable only used in DrawBackground}
- var
- thePat: PixPatHandle;
-
- procedure DrawBackground;
- const
- patID = 128;
- var
- r: Rect;
- mycolorFlag: Boolean;
-
- {A little routine for setting the forecolor with a single line.}
- procedure OTForeColor (red, green, blue: integer);
- var
- theColor: RGBColor;
- begin
- theColor.red := red;
- theColor.green := green;
- theColor.blue := blue;
- RGBForeColor(theColor);
- end;
-
- var
- savePort: SATPort;
-
- begin {DrawBackground}
- SATGetPort(savePort);
-
- SATSetPortBackScreen;
-
- {Do some drawing in backScreen. First, we paint a pattern (using a 'ppat' resource):}
-
- {For drawing the background, let's make a local flag that tells us if we shold draw}
- {b/w patterns or color ones.}
- if gColorQDFlag then
- mycolorFlag := gSAT.initDepth > 1
- else
- mycolorFlag := false;
-
- if mycolorFlag then
- begin
- if thePat = nil then
- thePat := GetPixPat(patID);
- PenPixPat(thePat)
- end
- else
- begin
- if thePat = nil then
- thePat := PixPatHandle(GetResource('ppat', patID));
- PenPat(thePat^^.pat1Data);
- end;
- PaintRect(gSAT.backScreen.port^.portRect);
- PenNormal;
-
- {Then we draw some circles.}
-
- r := gSAT.backScreen.port^.portRect;
- InsetRect(r, (r.right - r.left) div 8, (r.bottom - r.top) div 8);
- gBowlSize := longint(r.right - r.left) * (r.right - r.left) div 4; {Tells how big the "bowl" is!}
- if mycolorFlag then
- begin
- OTForeColor(-10000, -10000, -10000);
- PaintOval(r);
- end
- else
- {$IFC UNDEFINED THINK_PASCAL}
- FillOval(r, qd.ltGray);
- {$ELSEC}
- FillOval(r, ltGray);
- {$ENDC}
-
- InsetRect(r, (r.right - r.left) div 8, (r.bottom - r.top) div 8);
- if mycolorFlag then
- begin
- OTForeColor(-25000, -25000, -25000);
- PaintOval(r);
- end
- else
- {$IFC UNDEFINED THINK_PASCAL}
- FillOval(r, qd.gray);
- {$ELSEC}
- FillOval(r, gray);
- {$ENDC}
-
- InsetRect(r, (r.right - r.left) div 6, (r.bottom - r.top) div 6);
- if mycolorFlag then
- begin
- OTForeColor(20000, 20000, 20000);
- PaintOval(r);
- end
- else
- {$IFC UNDEFINED THINK_PASCAL}
- FillOval(r, qd.dkGray);
- {$ELSEC}
- FillOval(r, dkGray);
- {$ENDC}
-
- InsetRect(r, (r.right - r.left) div 5, (r.bottom - r.top) div 5);
- if gSAT.colorFlag then
- OTForeColor(0, 0, 0);
- PaintOval(r);
- { InsetRect(r, (r.right - r.left) div 5, (r.bottom - r.top) div 5);}
- { if mycolorFlag then}
- { begin}
- { OTForeColor(0, 0, 0);}
- { PaintOval(r);}
- { end}
- { else}
- { FillOval(r, black); }
-
- SATSetPortOffScreen;
- CopyBits(gSAT.backScreen.port^.portBits, gSAT.offScreen.port^.portBits, gSAT.backScreen.port^.portRect, gSAT.backScreen.port^.portRect, srcCopy, nil);
-
- SATSetPort(savePort);
- end; {DrawBackground}
-
- {DoBackground: repeating tasks - this is called repeatedly, after every event we get.}
-
- {Note: If you are making a really Mac-friendly program, this is where you should drive}
- {the animation. However, it is hard to get high framerate then, since other programs}
- {(the Finder included) will process events which will make it less smooth.}
-
- procedure DoBackground;
- var
- tmpRect: Rect;
- i, j: integer;
- vector: Point;
- saveGD: GDHandle;
- savePort: GrafPtr;
- tmpSpeed: Point;
- begin {DoBackground}
-
- SATRun(gFast); {Eller konfigurerbart?}
-
- end; {DoBackground}
-
-
- {DoUpdate: handle update events, in this case by copying offScreen to the screen (gWind).}
-
- {Note to beginners: A program without update events processing is not a real Mac program!}
- {All drawing you do must reach the update event handler in some way, or it might be lost,}
- {or worse, partially erased, which is really ugly.}
-
- procedure DoUpdate;
- var
- saveGD: GDHandle;
- savePort: GrafPtr;
- begin
- if SATDepthChangeTest then
- DrawBackground;
- BeginUpdate(gWind);
- SATRedraw;
- EndUpdate(gWind);
- end;
-
- {DoAppleMenu and DoFileMenu: handle menu selections}
-
- procedure DoAppleMenu (item: integer);
- var
- str: Str255;
- h: Handle;
- savePort: SATPort;
- ignore: integer;
- begin
- if item = 1 then
- begin
- if Alert(kAboutAlertID, nil) = 1 then
- ; {Ignore result}
- end
- else
- {Apple menu other than "About": Code from TransSkel}
- begin
- SATGetPort(savePort);
- GetMenuItemText(appleMenu, item, str); {Old intf: GetItem}
- SetResLoad(false);
- h := GetNamedResource('DRVR', str);
- SetResLoad(true);
- if h <> nil then
- begin
- ReserveMem(GetResourceSizeOnDisk(h) + $1000); {Old intf: ResrvMem, SizeResource}
- ignore := OpenDeskAcc(str);
- end;
- SATSetPort(savePort);
- end;
- end; {DoAppleMenu}
-
- procedure DoFileMenu (item: integer);
- var
- start, finish, frames: Longint;
- {fpsStr: Str255;}
- begin
- case item of
- 1:
- {Run animation without event processing until the user clicks the mouse}
- {Note: This runs the animation at maximum speed. In real programs, we}
- {must limit the speed with the system clock, e.g. inspect TickCount.}
- begin
- start := TickCount;
- frames := 0;
- while not Button do
- begin
- DoBackground;
- frames := frames + 1;
- end;
- finish := TickCount;
- SATReportStr(StringOf(frames * 60 div (finish - start):1, ' frames/second'));
- end;
- 2:
- begin
- gCollisionFlag := not gCollisionFlag;
- CheckItem(fileMenu, 2, gCollisionFlag);
- end;
- 3:
- begin
- gFast := not gFast;
- CheckItem(fileMenu, 3, gFast);
- end;
- 4:
- begin
- gSoundFlag := not gSoundFlag;
- CheckItem(fileMenu, 4, gSoundFlag);
- end;
- {Set the flag that tells the program to quit.}
- 6:
- gWhoa := true;
- end; {case}
- end; {DoFileMenu}
-
- { --- PART 4: Event processing: -----------------------------------------}
-
- {MenuSelection: Menu selection by mouse or command-key:}
-
- procedure MenuSelection (whatSelection: longInt);
- begin
- case HiWord(whatSelection) of
- kAppleID:
- DoAppleMenu(LoWord(whatSelection));
- kFileID:
- DoFileMenu(LoWord(whatSelection));
- end; {case}
- HiLiteMenu(0);
- end;
-
- {MainLoop: get and process events. This is the boring standard part of all programs. I prefer}
- {using TransSkel to get rid of it. I don't here since I want this code to be stand-alone.}
-
- procedure MainLoop;
- const
- kSleep = 5; {Real programs may modify the sleep time depending on whether or not they are in the front}
- var
- hasEvent: Boolean;
- theEvent: EventRecord;
- theKey: Char;
- whatSelection: Longint;
- whichPart: integer;
- whichWindow: WindowPtr;
- r: rect;
- p: Point;
- begin
- {Get the next event. Use WaitNextEvent if possible.}
- if gHasWNE then
- hasEvent := WaitNextEvent(everyEvent, theEvent, kSleep, nil)
- else
- begin
- SystemTask;
- hasEvent := GetNextEvent(everyEvent, theEvent);
- end;
-
- {OK, so what happened then?}
- if hasEvent then
- case theEvent.what of
- mouseDown:
- begin
- whichPart := FindWindow(theEvent.where, whichWindow);
- case whichPart of
- inMenuBar:
- begin
- whatSelection := MenuSelect(theEvent.where);
- MenuSelection(whatSelection);
- end;
- inSysWindow:
- SystemClick(theEvent, whichWindow);
- inGoAway:
- if (TrackGoAway(whichWindow, theEvent.where)) then
- gWhoa := true;
- inDrag:
- begin
- if (whichWindow <> FrontWindow) and (BitAnd(theEvent.modifiers, cmdKey) = 0) then
- SelectWindow(whichWindow);
- r := gSAT.bounds; {How big is the screen?}
- {Was: screenBits.bounds;{How big is the screen? (Note: Don't use screenBits for other things: it isn't a valid BitMap any more!)}
- r.top := r.top + kMBarHeight; { Skip down past menu bar }
-
- InsetRect(r, 4, 4);
-
- { LIMIT THE DRAGGING so no part of the window can get outside the screen! Cut down on r}
- {depending on where the click is? This is necessary if we use "fast mode", that is if we do}
- {SATRun(true) or any other operation with custom blitters.}
- SetPort(whichWindow);
- p := theEvent.where;
- GlobalToLocal(p);
- r.bottom := r.bottom - (whichWindow^.portRect.bottom - p.v);
- r.left := r.left + p.h;
- r.right := r.right - (whichWindow^.portRect.right - p.h);
-
- DragWindow(whichWindow, theEvent.where, r);
-
- if whichWindow = gSAT.wind.port then
- SATWindMoved;
-
- end;
- inGrow:
- ; {Ignored - we don't resize}
- inContent:
- if (whichWindow <> FrontWindow) then
- SelectWindow(whichWindow)
- else
- DoMouse(theEvent.where, theEvent.modifiers); {Go to application-specific mouse down handling}
- end; {case whichPart}
- end; {mouseDown}
- keyDown, autoKey:
- begin
- theKey := char(BitAnd(theEvent.message, charCodeMask));
- if (BitAnd(theEvent.modifiers, cmdKey) <> 0) then
- MenuSelection(MenuKey(theKey))
- else
- DoKey(theKey, theEvent.modifiers);
- end;
- updateEvt:
- {There's only one window to bother with here, but let's make sure that's the one the Mac wants to update.}
- if WindowPtr(theEvent.message) = gWind then
- DoUpdate;
- {Handle disk inserts like TransSkel.}
- diskEvt:
- if (HiWord(theEvent.message) <> noErr) then
- begin
- DILoad;
- if DIBadMount(Point($00400040), theEvent.message) = 0 then
- ;
- DIUnload;
- end; {diskEvt}
- otherwise {Other events are ignored}
- end; {case}
-
- DoBackground;
- end;
-
- { --- PART 5: Initializations: -----------------------------------------}
-
- {OTInit: Initialize global flags, menus and window}
-
- procedure OTInit;
- const
- {Trap numbers}
- _WaitNextEvent = $A860;
- _GetCIcon = $AA1E; {E.g. any Color QuickDraw routine}
- k32bQD = $AB1D;
- _SndPlay = $A805;
- begin
- {In case this isn't Think Pascal we have to make the standard inits ourselves.}
- {$IFC UNDEFINED THINK_PASCAL}
- SATInitToolbox;
- {$ENDC}
-
- gHasWNE := SATTrapAvailable(_WaitNextEvent);
- gColorQDFlag := SATTrapAvailable(k32bQD) and SATTrapAvailable(_GetCIcon); {???}
- gWhoa := false;
- gCollisionFlag := false;
-
- {gSoundFlag := TrapAvailable(_SndPlay); – Let SAT decide if we CAN or not!}
- gSoundFlag := true;
-
- {What more should I check for? Check with Gestalt instead?}
-
- {$IFC UNDEFINED THINK_PASCAL}
- qd.randSeed := TickCount; {Seed the random number generator - TickCount is good enough.}
- {$ELSEC}
- randSeed := TickCount; {Seed the random number generator - TickCount is good enough.}
- {$ENDC}
-
- {Get the window, a color window if we are going to use color.}
- if gColorQDFlag then
- gWind := GetNewCWindow(kWindId, nil, WindowPtr(-1))
- else
- gWind := GetNewWindow(kWindId, nil, WindowPtr(-1));
-
- {Some menus. We could read these from resources.}
- appleMenu := NewMenu(kAppleID, stringof(char($14)));
- AppendMenu(appleMenu, 'About Offscreen Toys SAT…;(-');
- AppendResMenu(appleMenu, 'DRVR'); {Old intf: AddResMenu}
- InsertMenu(appleMenu, 0); { put apple menu at end of menu bar }
- fileMenu := NewMenu(kFileID, 'File');
- AppendMenu(fileMenu, 'Try max speed;Collisions;Use SAT blitters;Use sound;(-;Quit/Q');
- InsertMenu(fileMenu, 0); { put file menu at end of menu bar }
- DrawMenuBar;
- CheckItem(fileMenu, 4, gSoundFlag);
- end;
-
- {OTOffscreensInit: Initialize offscreen grafports (worlds) and draw in them.}
-
- procedure OTOffscreensInit;
- var
- savePort: SATPort;
- r: Rect;
- i: integer;
- sp: SpritePtr;
- begin {OTOffscreensInit}
- SATConfigure(false, kNoSort, kForwardOneCollision, 32); {Only call *one* hitTask, not both.}
- SATCustomInit(0, 0, gWind^.portRect, gWind, nil, false, false, false, true, false);
-
- DrawBackground;
-
- {Done drawing!}
- {For your own hacks, consider using a PICT resource and use GetPicture and DrawPicture to draw the}
- {background. Note that you'll need both a color and a b/w picture if you want it to look good in b/w.}
-
- SATSetPortScreen;
-
- {Get the cicn resource}
- {Note: You can, of course, use several cicns and switch between.}
- gCicn := SATGetFace(128);
- kgck := SATGetNamedSound('Kgck');
-
- {Initialize the sprites:}
-
- for i := 1 to kSpriteNumber do
- sp := SATNewSprite(1, SATRand(gSAT.offScreen.port^.portRect.right - 32), (i - 1) * (gSAT.offScreen.port^.portRect.bottom - 32) div 5 + SATRand((gSAT.offScreen.port^.portRect.bottom - 32) div 5), @SetupMarble);
-
- if SATSoundInitChannels(2) < 2 then
- SysBeep(1);
- end;
-
- { --- MAIN PROGRAM BODY: -----------------------------------------}
-
- begin
- OTInit; {General initializations}
- OTOffscreensInit; {Set up the offscreen grafports}
- InitCursor; {Set the cursor to arrow in case it isn't.}
- SATSetPortScreen; {The front window is a good port to use.}
-
- {Run until quit or click in the close box.}
- repeat
- SATSetPortScreen; {The front window is a good port to use.}
- MainLoop;
- until gWhoa;
-
- {No cleanup is necessary here.}
- {We could DisposeGWorld, but that isn't necessary when we are quitting.}
- SATSoundShutup;
- end.